#include "wds_enemy.h"
#include "wds_pickup_manager.h"
#include "wds_player.h"
#include "wds_enemy_manager.h"
#include "wds_enemy_collection.h"
#include "wds_bullet.h"
#include "wds_enemy_manager.h"
#include "wds_fade_blood.h"

#include "dd/dd_man.h"
#include "math/facing.h"
#include "math/intersection.h"
#include "num/rand.h"
#include "application/game_script.h"
#include "console/console.h"
#include "ui/ui_manager.h"
#include "gameflow/game_flow_manager.h"
#include "font/font_manager.h"
#include "script/scripthelper.h"
#include "input/keyboard.h"
#include "graphics/graphics_util.h"
#include "luaplus/LuaObject.h"
#include "script/write_lua.h"
#include "lang/lang.h"
#include "vfs/vfs.h"
#include "script/file_list.h"

#include "util/mmgr.h"				// Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"				// logging
#include "util/timer.h"				// logging



void	WDSEnemy::Tick_States(float dt, const math::Vec2& Offset)
{
	switch(m_EnemyState)
	{
		case ENEMY_STATES_NONE:
			if(IsInsideScreen())
			{
				if(m_IsLunatic)
				{
					m_EnemyState = ENEMY_STATES_CHASE_lUNATIC_START;
				}
				else if( m_IsCrawl)
				{
					m_EnemyState = ENEMY_STATES_CHASE_CRAWL_START;
				}
				else
				{
					m_EnemyState = ENEMY_STATES_CHASE_BEFORE_START;
				}
			}
			m_SpeedScale = 0;
			Tick_States(dt,Offset);
		break;

		
		case ENEMY_STATES_GETHIT_START:
		{
			int Random = num::RAND32::Instance().RandInt(0,4);
			if(Random == 0)
			{
				m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_START;	
			}
			else
			{
				Random = num::RAND32::Instance().RandInt(0,2);
				if( Random == 0)
					SetAnimation("gethit1");
				else if(Random == 1)
					SetAnimation("gethit2");
				else if(Random == 2)
					SetAnimation("gethit3");
				else
					m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_START;							

				m_NextStateDelay = m_Sprite.GetTotalAnimTime();
				m_Movement.Stop();
				m_EnemyState = ENEMY_STATES_GETHIT;
			}
		}
		break;

		case ENEMY_STATES_GETHIT:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_GETHIT_END;	
				}
			}
		break;

		case ENEMY_STATES_GETHIT_END:	
		{
			int Random = num::RAND32::Instance().RandInt(0,1);
			if( Random == 0)
				m_EnemyState = ENEMY_STATES_NONE;
			else if(Random == 1)
				m_IsLunatic = true;		
				
			Tick_States(dt,Offset);
		}	
		break;

		case ENEMY_STATES_CHASE_BEFORE_START:
			
			m_EnemyState = ENEMY_STATES_CHASE_BEFORE;
			SetAnimation("chase_before");			
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();
		break;
					
		case ENEMY_STATES_CHASE_BEFORE:		
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_CHASE_START;
					Tick_States(dt,Offset);
				}
			}			
			Tick_Chase(dt,Offset);
		break;

		case ENEMY_STATES_CHASE_START:
			m_EnemyState = ENEMY_STATES_CHASE;
			SetAnimation("chase");
		
		break;

		case ENEMY_STATES_CHASE:					
			Tick_Chase(dt,Offset);
			
		break;		

		case ENEMY_STATES_CHASE_lUNATIC_START:
			SetAnimation("chase_lunatic");		
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();
			m_IsLunatic = true;
			m_EnemyState = ENEMY_STATES_CHASE_lUNATIC;
		break;

		case ENEMY_STATES_CHASE_lUNATIC:	
			Tick_Chase(dt,Offset);		
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState =  ENEMY_STATES_CHASE_lUNATIC_PAUSE;
					SetAnimation("chase_lunatic_pause"); 				
					m_NextStateDelay = 0.1f;
				}
			}
		break;

		case ENEMY_STATES_CHASE_lUNATIC_PAUSE:		
			m_Movement.Stop();
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_CHASE_lUNATIC_START;
					Tick_States(dt,Offset);
				}
			}

		break;

		case ENEMY_STATES_GETHIT_TO_CRAWL_START:
			m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_GETHIT_START;
			m_Movement.Stop();
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_GETHIT_TO_CRAWL_GETHIT_START:
			SetAnimation("gethit3");			
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();			
			m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_GETHIT; 
			m_Facing.SetTurnSpeed(m_TurnSpeed * 0.5f);
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_GETHIT_TO_CRAWL_GETHIT:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_CRAWL_BEFORE_START;					
					Tick_States(dt,Offset);
				}
			}
		break;
		
		case ENEMY_STATES_GETHIT_TO_CRAWL_CRAWL_BEFORE_START:
			SetAnimation("crawl_before");
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();	
			m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_CRAWL_BEFORE; 
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_GETHIT_TO_CRAWL_CRAWL_BEFORE:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_GETHIT_TO_CRAWL_END;
					Tick_States(dt,Offset);
				}
			}
		break;

		case ENEMY_STATES_GETHIT_TO_CRAWL_END:
			m_EnemyState = ENEMY_STATES_CHASE_CRAWL_START;
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_CHASE_CRAWL_START:
			SetAnimation("chase_crawl");			
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();
			m_IsCrawl = true;			
			m_EnemyState = ENEMY_STATES_CHASE_CRAWL;
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_CHASE_CRAWL:
			m_SpeedScale = 0.5f;			
			Tick_Chase(dt,Offset);
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState =  ENEMY_STATES_CHASE_CRAWL_PAUSE;
					SetAnimation("chase_crawl_pause"); 
					m_NextStateDelay = 0.2f;
				}
			}
		break;

		case ENEMY_STATES_CHASE_CRAWL_PAUSE:
			m_Movement.Stop();
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_CHASE_CRAWL_START;
				}
			}
		break;

		case ENEMY_STATES_ATTACK_START:
		{
			if(m_IsCrawl)
			{
				SetAnimation("attack_crawl"); 
			}
			else
			{
				int Random = num::RAND32::Instance().RandInt(0,1);
				if( Random == 0)
					SetAnimation("attack1"); 
				else if(Random == 1)
					SetAnimation("attack2"); 
				m_Facing.SetTurnSpeed(m_TurnSpeed * 2.0f);
			}
			
		
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();
			m_AttackHalfTime = m_NextStateDelay * 0.5f;
			m_EnemyState = ENEMY_STATES_ATTACK;
			m_Movement.Stop();
			
		}
		break;

		case ENEMY_STATES_ATTACK:
		if(m_NextStateDelay > 0)
		{
			m_NextStateDelay -= dt;
			
			if(m_AttackHalfTime > 0)
			{
				m_AttackHalfTime-= dt;
				if( m_AttackHalfTime <= 0)
				{
					CheckAttackHit(1.0f);
				}
			}

			if(m_NextStateDelay <= 0)
			{
				m_EnemyState = ENEMY_STATES_ATTACK_END;
			}

			WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
			if(pPlayer)
			{
				
				const math::Vec2 Dir = pPlayer->GetPos() - GetPos();	
				float Degree = math::Facing::AngleVector( Dir);
				this->Turn(Degree);	
			}
		}
		break;
		case ENEMY_STATES_ATTACK_END:
		{
			WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();			
			if(pPlayer->IsAlive())
			{
				math::Vec2 Dir = pPlayer->GetPos() - GetPos();
				Dir.y *= GraphicsUtil::H2W;
				if(Dir.Magnitude() < m_Sprite.m_SizeOriginal.x * 0.75f )
				{
					m_EnemyState = ENEMY_STATES_ATTACK_START;
				}
				else
				{
					m_EnemyState = ENEMY_STATES_NONE;
				}
				m_Facing.SetTurnSpeed(m_TurnSpeed);
			}
			else
			{
				m_EnemyState = ENEMY_STATES_CHASE;
				Tick_States(dt, Offset);
			}
		}
		break;

		case  ENEMY_STATES_DYING_START:
		{
			if(m_IsCrawl)
			{
				SetAnimation("dead_crawl");
			}
			else
			{
				const int random = num::RAND32::Instance().RandInt(0,2);
				if(random == 0)
					SetAnimation("dead1");
				else if(random == 1)
					SetAnimation("dead2");
				else 
					SetAnimation("dead3");
			}
			
			m_Movement.Stop();
			m_Movement.ClearForces();
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();		
			m_EnemyState = ENEMY_STATES_DYING;
		}
		break;

		case ENEMY_STATES_DYING:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					math::Vec2 TempPos = GetPos();
					math::Vec2 TempDir = m_Facing.GetForwardW2H();
					if(m_CurrentAnimation.compare("dead1") == 0)		// back
					{	
						TempPos -= ( TempDir * (m_Sprite.m_SizeOriginal.x * 0.4f) );
					}
					else if(m_CurrentAnimation.compare("dead2") == 0)		// front
					{
						TempPos += ( TempDir * (m_Sprite.m_SizeOriginal.x * 0.4f) );
					}

					m_EnemyState = ENEMY_STATES_DYING_END;
					WDSFadeBloodManager::Instance().Spawn( TempPos, num::RAND32::Instance().RandFloat(0,359.0f),1.0f);
					Tick_States(dt,Offset);
				}
			}
		break;


		case ENEMY_STATES_DYING_END:
		{
			char buffer[512];
			sprintf(buffer, "%s_after", m_CurrentAnimation.c_str());
			SetAnimation(buffer); 
			m_NextStateDelay = 1.0f;
			m_EnemyState = ENEMY_STATES_DEAD;
			
		}
		break;


		case ENEMY_STATES_DEAD:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_READYSPAWN;					
				}
			}
		break;

		case ENEMY_STATES_RISE_START:
		{
			m_DelaySpawn = num::RAND32::Instance().RandFloat(0,0.5f);
			const int random = num::RAND32::Instance().RandInt(0,2);
			if(random == 0)
				SetAnimation("rise1");
			else if(random == 1)
				SetAnimation("rise2");
			else 
				SetAnimation("rise3");

			m_Movement.Stop();
			m_Movement.ClearForces();
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();		
			m_EnemyState = ENEMY_STATES_RISE;
		}
		break;

		case ENEMY_STATES_RISE:
		if(m_NextStateDelay > 0)
		{
			m_NextStateDelay -= dt;
			if(m_NextStateDelay <= 0)
			{
				m_EnemyState = ENEMY_STATES_RISE_END;
				Tick_States(dt,Offset);
			}
		}
		break;

		case ENEMY_STATES_RISE_END:
			m_EnemyState = ENEMY_STATES_NONE;
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_EATING_INIT:
			m_EatCurrentLoop = 0;
			m_EatMaxLoop = num::RAND32::Instance().RandInt(2,5);
			m_EnemyState = ENEMY_STATES_EATING_START;
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_EATING_START:
		{
			SetAnimation("eating");			
			m_Movement.Stop();
			m_Movement.ClearForces();
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();		
			m_EnemyState = ENEMY_STATES_EATING;
			if(m_EatCurrentLoop == 0)
			{				
				const float TEMP_DT = num::RAND32::Instance().RandFloat(0,1.0f);
				m_Sprite.Tick(TEMP_DT);		// we don't want all eating to be animating at the same frame.
				m_NextStateDelay -= TEMP_DT;
			}			
		}
		break;
		
		case ENEMY_STATES_EATING:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_EATING_END;
					Tick_States(dt,Offset);
				}
			}
		break;

		case ENEMY_STATES_EATING_END:
			m_EatCurrentLoop++;
			if(m_EatCurrentLoop < m_EatMaxLoop)
			{
				m_EnemyState = ENEMY_STATES_EATING_START;				
			}
			else
			{
				WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
				if(pPlayer && pPlayer->IsAlive())
				{
					m_EatCurrentLoop = 0;
					m_EatMaxLoop = 0;
					m_EnemyState = ENEMY_STATES_EATING_STANDUP_START;
				}
				else
				{
					m_EnemyState = ENEMY_STATES_EATING_START;	
				}
			}
			Tick_States(dt,Offset);
		break;

		case ENEMY_STATES_EATING_STANDUP_START:
			SetAnimation("eating_standup");			
			m_Movement.Stop();
			m_Movement.ClearForces();
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();		
			m_EnemyState = ENEMY_STATES_EATING_STANDUP;
		break;

		case ENEMY_STATES_EATING_STANDUP:
		{
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_EATING_STANDUP_END;
					Tick_States(dt,Offset);
				}
			}
			
			WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
			if(pPlayer)
			{
				m_Facing.SetTurnSpeed(m_TurnSpeed * 0.35f);
				const math::Vec2 Dir = pPlayer->GetPos() - GetPos();	
				float Degree = math::Facing::AngleVector( Dir);
				this->Turn(Degree);	
			}
		}
		break;

		case ENEMY_STATES_EATING_STANDUP_END:

			m_EnemyState = ENEMY_STATES_IDLE_START;			
			m_Facing.SetTurnSpeed(m_TurnSpeed);			
			Tick_States(dt,Offset);
		break;


		case ENEMY_STATES_EATING_SITDOWN_START:
			SetAnimation("eating_sitdown");			
			m_Movement.Stop();
			m_Movement.ClearForces();
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();		
			m_EnemyState = ENEMY_STATES_EATING_SITDOWN;
		break;

		case ENEMY_STATES_EATING_SITDOWN:
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_EATING_SITDOWN_END;
					Tick_States(dt,Offset);
				}
			}
		break;

		case ENEMY_STATES_EATING_SITDOWN_END:
			m_EnemyState = ENEMY_STATES_EATING_INIT;
			Tick_States(dt,Offset);
		break;


		case ENEMY_STATES_IDLE_START:
			SetAnimation("idle");			
			m_Movement.Stop();
			m_Movement.ClearForces();
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();		
			m_EnemyState = ENEMY_STATES_IDLE;			
		break;

		case ENEMY_STATES_IDLE:
		{
			if(m_NextStateDelay > 0)
			{
				m_NextStateDelay -= dt;
				if(m_NextStateDelay <= 0)
				{
					m_EnemyState = ENEMY_STATES_IDLE_END;
					Tick_States(dt,Offset);
				}
			}
			
			WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
			if(pPlayer)
			{
				m_Facing.SetTurnSpeed(m_TurnSpeed * 0.35f);
				const math::Vec2 Dir = pPlayer->GetPos() - GetPos();	
				float Degree = math::Facing::AngleVector( Dir);
				this->Turn(Degree);	
			}
		}
		break;

		case ENEMY_STATES_IDLE_END:
			m_EnemyState = ENEMY_STATES_NONE;
			Tick_States(dt,Offset);
			m_Facing.SetTurnSpeed(m_TurnSpeed);
		break;
			
	}
}

void	WDSEnemy::Tick_Idle(float dt, const math::Vec2& Offset)
{
	// search for player within a mid-range distance
	// check line of sight?
	

	bool Found = true;

	if(Found)
	{
		m_EnemyState = ENEMY_STATES_CHASE;
		Tick_Chase(dt,Offset);
		WDSLifeObject::Tick(dt);
	}
}

void	WDSEnemy::Tick_Wander(float dt, const math::Vec2& Offset)
{

}

void	WDSEnemy::Tick_Chase(float dt, const math::Vec2& Offset)
{
	if(m_EnemyState != ENEMY_STATES_CHASE_CRAWL && m_SpeedScale < 1.0f)
	{
		m_SpeedScale += dt;
		if(m_SpeedScale > 1.0f)
			m_SpeedScale = 1.0f;
	}


	if(m_NextMovementTargetDealy > 0)
	{
		m_NextMovementTargetDealy -= dt;
		return;
	}

	// keep slowing facing towards player and go forward.

	// if somone is blocking my way, 
	// I have to check side ways, if someone is not there, I have to move toward that side
	
	float TempDegree = 0;
	if(IsOtherEnemyInThatDegree(m_Facing.GetAngle(), m_Sprite.m_SizeOriginal.x, true ))
	{
		
		if( num::RAND32::Instance().RandInt(0,1) == 0)
		{
			TempDegree = m_Facing.GetAngle() + 90.0f;
			if( IsOtherEnemyInThatDegree(TempDegree, m_Sprite.m_SizeOriginal.x * 2.0f , true ) == false)
			{
				AvoidMovement(TempDegree);
				return;
			}
		}
		else
		{
			TempDegree = m_Facing.GetAngle() - 90.0f;
			if( IsOtherEnemyInThatDegree(TempDegree, m_Sprite.m_SizeOriginal.x * 2.0f , true) == false)
			{
				AvoidMovement(TempDegree);
				return;
			}
		}
		
	}

	WDSPlayer* pPlayer = WDSEnemyManager::Instance().GetPlayer();
	if(pPlayer)
	{
		
		math::Vec2 Dir = pPlayer->GetPos() - GetPos();
		Dir.y *= GraphicsUtil::H2W;
		if(Dir.Magnitude() < m_Sprite.m_SizeOriginal.x * 0.75f && pPlayer->IsAlive() )
		{
			m_EnemyState = ENEMY_STATES_ATTACK_START;
			return;
		}
		else if(Dir.Magnitude() < m_Sprite.m_SizeOriginal.x * 0.6f && !pPlayer->IsAlive() )
		{
			m_EnemyState = ENEMY_STATES_EATING_SITDOWN_START;
			return;
		} 


		float Degree = math::Facing::AngleVector( Dir);
		this->Turn(Degree);		
		m_Movement.SetTarget(m_Facing.GetAngle());
		if( m_CurrentAnimation.compare("walk") == 0)
		{
			m_EnemyState = ENEMY_STATES_CHASE_BEFORE;
			SetAnimation("chase_before");			
			//SetAnimation("chase");			
			m_NextStateDelay = m_Sprite.GetTotalAnimTime();
		}
		//m_NextMovementTargetDealy = num::RAND32::Instance().RandFloat(0,1.0f);
	}
}


